package com.lnt.spring.service;

import com.lnt.spring.bean.DeptTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

/**
 * @title : Spring Autowired依赖注入
 * @createDate : 2021-11-23 14:40
 * @creater : LuNanTing
 * @className : AutowiredInjectServiceTest
 */

/**
 * @Autowired可以加在全局变量上，也可以加载set方法上，通过@Autowired可以告诉Spring依赖注入点
 * @Autowired加在成员变量上，Spring会根据变量属性和变量名称去getBean来注入
 * @Autowired加在set方法上，Spring会根据方法参数的类型和名称来getBean对应的参数，从而注入(set方法可以有多个参数)
 *
 */
@Component
public class AutowiredInjectServiceTest {

    /**
     * Spring查找注入点的方法:
     * 1.通过启动Spring来执行创建bean的生命周期来补充bean属性
     * 2.查找注入点的方法是Spring内部实现BeanPostProcessor的接口实现的
     * 3.实现类AutowiredAnnotationBeanPostProcessor通过实现了接口MergedBeanDefinitionPostProcessor的方法
     * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
     * 4.在AutowiredAnnotationBeanPostProcessor.java中，Spring先执行了postProcessMergedBeanDefinition(RootBeanDefinition,Class<?>,String )
     * 方法下的findAutowiringMetadata(beanName, beanType ,null);来找注入点
     * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata()
     * 5.紧接着会执行postProcessProperties下相同的findAutowiringMetadata(beanName, beanType, null)
     * 注: * findAutowiringMetadata(beanName, beanType ,null)会将找到的方法缓存
     *
     */

    /**
     * 执行注入属性：
     * 1.在执行属性注入最主要的方法是AutowiredAnnotationBeanPostProcessor.java下的buildAutowiringMetdata(Class clazz)方法
     * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
     * 2.首先判断Bean的类型是不是String等简单属性，如果为true，则不需要进行依赖注入
     * 3.遍历当前类的所有字段，然后判断该字段上是否存在@Autowired,@Value,@Inject注解，如果有，就会把该字段当成一个
     * 注入点，构造成一个AutowiredFieldElement(field, required)对象，加入到currElements集合中
     * 4.遍历当前类的所有set方法,然后判断该方法上是否有@Autowired,@Value,@Inject注解，如果有，就会把该方法当成一个
     * 注入点,并且构造成一个AutowiredMethodElement(method, required, pd),加入到currElements集合中
     * 5.将循环当前类的父类，并执行上部分操作，这时候就拿到了所有的注入点及其相关属性
     * 6.执行inject进行注入:
     *      <1>获取所有的currElements集合List<InjectedElement>并循环执行InjectedElement.inject()方法</1>
     *      @see org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement
     *      <2>具体的inject 方法要看InjectedElement抽象类下的具体实现类，分别是</2>
     *      <2>字段注入的实现类AutowiredFieldElement</2>
     *      <2>方法注入的实现类AutowiredMethodElement</2>
     *      @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor .AutowiredFieldElement
     *      @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor .AutowiredMethodElement
     *      <3>在各自injuect的方法中有一处代码:</3>
     *          (1)
     *              if (this.cached) {
     *                  try {
     * 					    value = resolvedCachedArgument(beanName, this.cachedFieldValue);
     *                  }catch (NoSuchBeanDefinitionException ex) {
     * 					    // Unexpected removal of target bean for cached argument -> re-resolve
     * 					    value = resolveFieldValue(field, bean, beanName);
     *                  }
     *              }
     *          (2) 该方法第一次进行执行的时候cache为false,表示没有缓存,则执行catch中的resolveFieldValue/resolveMethodValue方法
     *          resolveFieldValue/resolveMethodValue 方法执行的过程为：
     *          (3) 该方法内部的执行过程为:
     *              (①).首先将参数中的参数/字段和require属性封装成依赖描述DependencyDescriptor.class,
     *              (②). 通过方法获取到Spring的bean对象并返回，
     *
     */
    @Autowired
    private String userName;

    @Autowired
    @Lazy
    /**
     * 在使用@Autowired注解是，会解析该变量/方法参数是否使用了@Lazy注解，
     * 如果使用了该注解，那么在注入过程中会注入该属性的代理对象,只有在使用该参数/变量时,才会通过代理对象获取对应的bean对象
     */
    private UserService userService;

    private OrderService orderService;

    /**
     * Spring 在进行依赖注入的时候，会考虑变量/方法参数上 是否有@Value的注解，
     * 1.会先获取@Value的注解内容是什么，
     * 2.根据获取的value值来判断是否继承String
     * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
     *      <1>判断是否是占位符填充${},如果是则进行解析，并通过配置文件或者项目制定的VM来组成对应的key-value来获取</1>
     *      #<1>VM的优先级比配置文件的高</1>#
     *      #<1>如果配置文件和VM都没有制定对应的key,则返回的是 将${xxx}作为字符串赋值给变量</1>#
     *      @see org.springframework.beans.factory.support.DefaultListableBeanFactory doResolveDependency()/Line:1321
     *      <2>Spring表达式填充 #{}, 通过表达式#{xxx}中的xxx来获取Spring中的命名为xxx的bean对象，并解析注入</2>
     *      @see org.springframework.beans.factory.support.DefaultListableBeanFactory doResolveDependency()/Line 1324
     * 3. 将value的值通过TypeConversion(类型转换器)转为对应的属性类型
     * 4. 如果@Vaule的变量/参数类型是数组/Map/集合，那么就将descriptor(注入描述适配器)返回，不做进一步的筛选了
     * 5. 通过findAutowireCandidates方法找到所有的bean,key是beanName,value可能是bean对象，也可能是beanClass
     * 6. 
     */
    @Autowired
    @Value("${password}")
    private String password;

    @Autowired
    @Value("#{deptTest}")
    private DeptTest dept;

    @Autowired
    private void setOrderService(OrderService orderService){
        this.orderService = orderService;
    }

    public void test(){
        System.out.println("password:" + this.password);
        System.out.println("dept:" + this.dept);
        System.out.println("userService.a():" );
        userService.a();
    }

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}
